/*
 * Copyright (c) 2017-2019, Lindenis Tech. Ltd.
 * All rights reserved.
 *
 * File:
 *
 * Description:
 *
 * Author:
 *      xiaoshujun@lindeni.com
 *
 * Create Date:
 *      2019/12/20
 *
 * History:
 *
 */

#ifndef __LIND_MODBUS_WRAPPER_H_
#define __LIND_MODBUS_WRAPPER_H_

#ifdef __cplusplus
extern "C" {
#endif

typedef long ldmb_handle;
typedef unsigned int ldmb_slave_device_type;
typedef unsigned int ldmb_ops_type;

#define	TRUE    1
#define	FALSE   0

typedef enum _ldmb_device_status_e
{
    LDMB_DEVICE_OFF     = 0,
    LDMB_DEVICE_ON      = 1,
} ldmb_device_status_e;

typedef struct _ldmb_device_map_t
{
    ldmb_slave_device_type  device;
    char                    name[64];
    int                     slave_addr;
} ldmb_device_map_t;

typedef enum _ldmb_register_type_e
{
    LDMB_REG_TYPE_DO        = 0x0001,
    LDMB_REG_TYPE_DI        = 0x0002,
    LDMB_REG_TYPE_AO        = 0x0004,
    LDMB_REG_TYPE_AI        = 0x0008,
} ldmb_register_type_e;

typedef enum _ldmb_ops_type_e
{
    LDMB_OPS_READ           = 0x0001,
    LDMB_OPS_WRITE          = 0x0002,
} ldmb_ops_type_e;

typedef struct _ldmb_func_map_t
{
    ldmb_ops_type           ops;
    char                    description[128];
    ldmb_slave_device_type  device;
    ldmb_ops_type_e         rw;
    ldmb_register_type_e    reg_type;
    int                     reg_addr;
    int                     reg_nb;
    int                     delay_us;
} ldmb_func_map_t;

typedef struct _ldmb_configs_t
{
    /*
     * 1/0: print or not print communication log
     */
    int debug;

    /*
     * RTU or TCP, now only support RTU on RS485
     */
    int protocol_type;

    /*
     * "/dev/ttyUSB0"
     */
    int tty_device[32];

    /*
     * 9600, ..., 115200
     */
    int baudrate;

    /*
     * parity bit: none: 'N', even: 'E', odd: 'O'
     */
    char parity;

    /*
     * 8 bits or 7 bits
     */
    int data_bit;

    /*
     * 1 stop bit or 2 stop bits
     */
    int stop_bit;

    /*
     * response timeout
     */
    int sec;
    int usec;
} ldmb_configs_t;

/**
 * Create a ldmb_handle object.
 *
 * @param
 * @return
 *    NULL: create failed
 *    not NULL: the ldmb_handle object
 */
ldmb_handle ldmb_create();

/**
 * Destory ldmb_handle object.
 *
 * @param
 *    handle[in]: the ldmb_handle handle.
 * @return
 */
void ldmb_destory(ldmb_handle handle);

/**
 * Set parameters for modbus.
 *
 * @param
 *    handle[in]: the ldmb_handle handle.
 *    p_config[in]: set modbus parameters.
 * @return
 *    0 success, < 0 error
 */
int ldmb_set_config(ldmb_handle handle, ldmb_configs_t * p_config);

/**
 * Modbus master scan and connect slave devices, create a map list of
 * modbus address to slave id.
 *
 * @param
 *    handle[in]: the ldmb_handle handle.
 * @return
 *    0 success, < 0 error
 */
int ldmb_connect(ldmb_handle handle, ldmb_device_map_t * pp_device_map, int device_cnt);

/**
 * Modbus master scan and connect slave devices, create a map list of
 * modbus address to slave id.
 *
 * @param
 *    handle[in]: the ldmb_handle handle.
 * @return
 *    0 success, < 0 error
 */
int ldmb_disconnect(ldmb_handle handle);

/**
 * Do operations defined in ldmb_func_map_t.
 *
 * @param
 *    handle[in]    : the ldmb_handle handle.
 *    p_func_map[in]: the operation needed to be execute.
 *    data[in/out]  : [out] result of read;
 *                    [in]  value to be written.
 * @return
 *    >= 0 success, < 0 error
 *    if return -1, unknown error
 *    if retrun other negative number(ret), 'error_code = abs(ret) - 100'
 */
int ldmb_do_ops(ldmb_handle handle,
                ldmb_func_map_t * p_func_map,
                void * data);

/**
 * Function code 0x01.
 *    Reads the boolean status of bits(Coil status/DOs) and sets the array elements
 *    in the destination to TRUE or FALSE (single bits).
 *
 * @param
 *    handle[in]    : the ldmb_handle handle.
 *    device[in]    : a device defined in ldmb_slave_device_type.
 *    reg_addr[in]  : register address of the coil status/DOs.
 *    reg_nb[in]    : numbers of register.
 *    dest[out]     : the data read.
 * @return
 *    return the numbers of reading registers.
 *    > 0 success, < 0 error
 */
int ldmb_read_bits(ldmb_handle handle,
                   ldmb_slave_device_type device,
                   int reg_addr,
                   int reg_nb,
                   uint8_t * dest);

/**
 * Function code 0x02.
 *    Same as ldmb_read_bits but reads the remote device input table(DIs).
 *
 * @param
 *    handle[in]    : the ldmb_handle handle.
 *    device[in]    : a device defined in ldmb_slave_device_type.
 *    reg_addr[in]  : register address of the input status/DIs.
 *    reg_nb[in]    : numbers of register.
 *    dest[out]     : the data read.
 * @return
 *    return the numbers of reading registers.
 *    > 0 success, < 0 error
 */
int ldmb_read_input_bits(ldmb_handle handle,
                    ldmb_slave_device_type device,
                    int reg_addr,
                    int reg_nb,
                    uint8_t * dest);

/**
 * Function code 0x03.
 *    Reads hoding registers from a remote device and put that data into an array.
 *
 * @param
 *    handle[in]    : the ldmb_handle handle.
 *    device[in]    : a device defined in ldmb_slave_device_type.
 *    reg_addr[in]  : register address of the hoding registers(AOs).
 *    reg_nb[in]    : numbers of register.
 *    dest[out]     : the data read.
 * @return
 *    >= 0 success, < 0 error
 */
int ldmb_read_registers(ldmb_handle handle,
                    ldmb_slave_device_type device,
                    int reg_addr,
                    int reg_nb,
                    uint16_t * dest);

/**
 * Function code 0x04.
 *    Reads input registers from a remote device and put that data into an array.
 *
 * @param
 *    handle[in]    : the ldmb_handle handle.
 *    device[in]    : a device defined in ldmb_slave_device_type.
 *    reg_addr[in]  : register address of the input registers(AIs).
 *    reg_nb[in]    : numbers of register.
 *    dest[out]     : the data read.
 * @return
 *    >= 0 success, < 0 error
 */
int ldmb_read_input_registers(ldmb_handle handle,
                    ldmb_slave_device_type device,
                    int reg_addr,
                    int reg_nb,
                    uint16_t * dest);

/**
 * Function code 0x05.
 *    Turns ON or OFF a single bit of the remote device.
 *
 * @param
 *    handle[in]    : the ldmb_handle handle.
 *    device[in]    : a device defined in ldmb_slave_device_type.
 *    reg_addr[in]  : register address of the coil status/DOs.
 *    status[in]    : 0 for OFF, 1 for ON.
 * @return
 *    > 0 success, < 0 error
 */
int ldmb_write_bit(ldmb_handle handle,
                    ldmb_slave_device_type device,
                    int reg_addr,
                    int status);

/**
 * Function code 0x06.
 *    Writes a value in one register of the remote device
 *
 * @param
 *    handle[in]    : the ldmb_handle handle.
 *    device[in]    : a device defined in ldmb_slave_device_type.
 *    reg_addr[in]  : register address of the hoding registers(AOs).
 *    value[in]     : the value to write.
 * @return
 *    > 0 success, < 0 error
 */
int ldmb_write_register(ldmb_handle handle,
                    ldmb_slave_device_type device,
                    int reg_addr,
                    const uint16_t value);

/**
 * Function code 0x15 (15).
 *    Turns ON or OFF multi-bits of the remote device.
 *
 * @param
 *    handle[in]    : the ldmb_handle handle.
 *    device[in]    : a device defined in ldmb_slave_device_type.
 *    reg_addr[in]  : register address of the coil status/DOs.
 *    reg_nb[in]    : numbers of register.
 *    data[in]      : 0 for OFF, 1 for ON, each register in a Byte.
 * @return
 *    > 0 success, < 0 error
 */
int ldmb_write_bits(ldmb_handle handle,
                    ldmb_slave_device_type device,
                    int reg_addr,
                    int reg_nb,
                    const uint8_t * data);

/**
 * Function code 0x10 (16).
 *    Write the values from the array to the registers of the remote device.
 *
 * @param
 *    handle[in]    : the ldmb_handle handle.
 *    device[in]    : a device defined in ldmb_slave_device_type.
 *    reg_addr[in]  : register address of the hoding registers(AOs).
 *    reg_nb[in]    : numbers of register.
 *    data[in]      : the value to write, each register in a Word.
 * @return
 *    > 0 success, < 0 error
 */
int ldmb_write_registers(ldmb_handle handle,
                    ldmb_slave_device_type device,
                    int reg_addr,
                    int reg_nb,
                    const uint16_t * data);

float ldmb_get_float(const uint16_t *src);

#ifdef __cplusplus
}
#endif

#endif // __LIND_MODBUS_WRAPPER_H_

